/*******************************************************************************
 Jimm - Mobile Messaging - J2ME ICQ clone
 Copyright (C) 2003-05  Jimm Project

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the  Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ********************************************************************************
 File: src/jimm/comm/Icq.java
 Version: ###VERSION###  Date: ###DATE###
 Author: Manuel Linsmayer, Andreas Rossbacher
 *******************************************************************************/

// #sijapp cond.if protocols_ICQ is "true" #
package org.jimm.protocol.icq;

import java.io.*;
import java.util.*;

import org.jimm.app.R;
import org.jimm.comm.*;
//import org.jimm.search.UserInfo;
//import org.jimm.ui.TextListEx;
//import org.jimm.search.Search;
import org.jimm.message.Message;
import org.jimm.message.PlainMessage;
import org.jimm.message.PluginMessage;
import org.jimm.message.SystemNotice;
import org.jimm.protocol.*;
import org.jimm.protocol.icq.plugin.XtrazMessagePlugin;
import org.jimm.protocol.icq.action.*;
import org.jimm.protocol.icq.packet.*;
import android.app.Activity;
import android.text.TextUtils;

public class Icq extends Protocol {

    public static final Integer[] statusIcons =
            {
                    R.drawable.icq_status_offline,
                    R.drawable.icq_status_online,
                    R.drawable.icq_status_away,
                    R.drawable.icq_status_ffc,
                    R.drawable.icq_status_home,
                    R.drawable.icq_status_work,
                    R.drawable.icq_status_evil,
                    R.drawable.icq_status_depression,
                    R.drawable.icq_status_lunch,
                    R.drawable.icq_status_na,
                    R.drawable.icq_status_occupied,
                    R.drawable.icq_status_dnd,
                    R.drawable.icq_status_invisible,
                    R.drawable.icq_status_invisible,
                    R.drawable.icq_status_invisible,
                    R.drawable.icq_status_connecting,
            };

    private IcqNetWorking connection = null;

    public String getUserIdName() {
        return "UIN";
    }

    protected void initStatusInfo() {
        info = new StatusInfo(statusIcons);
        // #sijapp cond.if modules_XSTATUSES is "true" #
        xstatusInfo = Icq.xstatus.getInfo();
        // #sijapp cond.end #
        // #sijapp cond.if modules_CLIENTS is "true" #
        clientMasking.setClient(p.clientId, null);
        // #sijapp cond.end #
    }

    public boolean isEmpty() {
        return super.isEmpty() || ((0 == Util.strToIntDef(getUserId(), 0)) && (getUserId().indexOf('@') <= 0));
    }

    public final void setRealUin(String uin) {
        if (!TextUtils.isEmpty(uin)) {
            setUserId(uin);
        }
    }


    public void requestSimpleAction(IcqAction act) {
        if (null != connection) {
            connection.requestAction(act);
        }
    }

    private void requestSimpleAction(Packet pkt) {
        requestSimpleAction(new OtherAction(pkt));
    }

    /**
     * Construct plain message object and request new SendMessageAction
     * Add the new message to the chat history
     */
    protected void sendSomeMessage(PlainMessage msg) {
        requestSimpleAction(new SendMessageAction(msg));
    }

    protected void s_addGroup(Group group) {
        IcqAction act = new UpdateContactListAction(group,
                UpdateContactListAction.ACTION_ADD);
        requestSimpleAction(act);
    }

    // Adds a IcqContact to the server saved contact list
    protected void s_addContact(Contact contact) {
        // Request contact item adding
        IcqAction act = new UpdateContactListAction(this, contact, UpdateContactListAction.ACTION_ADD);
        requestSimpleAction(act);
    }

    /**
     * Connects to the ICQ network
     */
    protected void startConnection() {
        connection = new IcqNetWorking();
        connection.initNet(this);
        ConnectAction act = new ConnectAction(this);
        connection.requestAction(act);
        act.initProgressBar();
        connection.start();
    }

    /**
     * Disconnects from the ICQ network
     */
    protected void closeConnection() {
        IcqNetWorking icqState = connection;
        connection = null;
        if (null != icqState) {
            icqState.disconnect();
        }
    }

    // Checks whether the comm. subsystem is in STATE_CONNECTED
    public boolean isConnected() {
        return (null != connection) && connection.isIcqConnected();
    }

    // Dels a IcqContact to the server saved contact list
    protected void s_removeContact(Contact cItem) {
        requestSimpleAction(new UpdateContactListAction(this, cItem,
                UpdateContactListAction.ACTION_DEL));
    }

    /**
     * ********************************************************************
     */
    protected void doAction(Contact contact, int action) {
        switch (action) {
            case IcqContact.USER_MENU_REMOVE_ME:
                // Remove me from other users contact list
                sendRemoveMePacket(contact.getUserId());
//                ContactList.getInstance().activate();
                break;

            // #sijapp cond.if modules_SERVERLISTS is "true" #
            case Contact.USER_MENU_PS_VISIBLE:
            case Contact.USER_MENU_PS_INVISIBLE:
            case Contact.USER_MENU_PS_IGNORE:
                int list = ServerListsAction.IGNORE_LIST;
                switch (action) {
                    case Contact.USER_MENU_PS_VISIBLE:
                        list = ServerListsAction.VISIBLE_LIST;
                        break;
                    case Contact.USER_MENU_PS_INVISIBLE:
                        list = ServerListsAction.INVISIBLE_LIST;
                        break;
                    case Contact.USER_MENU_PS_IGNORE:
                        list = ServerListsAction.IGNORE_LIST;
                        break;
                }
                requestSimpleAction(new ServerListsAction(list, (IcqContact) contact));
                //               ContactList.getInstance().activate();
                break;
            // #sijapp cond.end #
        }
    }

    /**
     * ********************************************************************
     */
    // #sijapp cond.if modules_XSTATUSES is "true" #
    public void requestXStatusMessage(StatusView statusView, Contact c) {
        StringBuffer str = new StringBuffer();
        str.append("<N><QUERY>");
        str.append(Util.xmlEscape("<Q><PluginID>srvMng</PluginID></Q>"));
        str.append("</QUERY><NOTIFY>");
        str.append(Util.xmlEscape("<srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>1</trans><senderId>"
                + getUserId()
                + "</senderId></req></srv>"));
        str.append("</NOTIFY></N>");
        XtrazMessagePlugin plugin = new XtrazMessagePlugin((IcqContact) c, str.toString());
        Message awayReq = new PluginMessage(this, plugin);
        requestSimpleAction(new SendMessageAction(awayReq));
        ((IcqContact) c).setXStatusMessage("");
        updateStatusView(statusView, c);
    }
    // #sijapp cond.end #

    /**
     * **********************************************************************
     */
    public boolean isMeVisible(Contact to) {
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        switch (getPrivateStatus()) {
            case StatusInfo.PSTATUS_NONE:
                return false;

            case StatusInfo.PSTATUS_NOT_INVISIBLE:
                return !to.inInvisibleList();

            case StatusInfo.PSTATUS_VISIBLE_ONLY:
                return to.inVisibleList();

            case StatusInfo.PSTATUS_CL_ONLY:
                return !to.isTemp();
        }
        // #sijapp cond.else #
        long status = getProfile().statusIndex;
        if ((StatusInfo.STATUS_INVISIBLE == status)
                || (StatusInfo.STATUS_INVIS_ALL == status)) {
            return false;
        }
        // #sijapp cond.end #
        return true;
    }

    public UserInfo getUserInfo(Contact c, Activity activity) {
        UserInfo data = new UserInfo(this, activity);
        RequestInfoAction getInfoAction = new RequestInfoAction(data, (IcqContact) c);
        requestSimpleAction(getInfoAction);
        return data;
    }

    /**
     * **********************************************************************
     */

    protected void s_updateOnlineStatus() {
        byte bCode = getPrivateStatusByStatus();
        // #sijapp cond.if modules_SERVERLISTS isnot "true" #
        if (-1 == bCode) {
            bCode = Icq.PSTATUS_NOT_INVISIBLE;
            sendPrivateStatus(bCode);
        }
        // #sijapp cond.end #
        sendCaps();
        // #sijapp cond.if modules_XSTATUSES is "true" #
        sendNewXStatus();
        // #sijapp cond.end #
        sendStatus();

        // #sijapp cond.if modules_SERVERLISTS isnot "true" #
        if (Icq.PSTATUS_NOT_INVISIBLE != bCode) {
            sendPrivateStatus(bCode);
        }
        // #sijapp cond.else #
        if (-1 != bCode) {
            setPrivateStatus((Icq.PSTATUS_NONE == bCode)
                    ? (byte) StatusInfo.PSTATUS_NONE
                    : (byte) StatusInfo.PSTATUS_VISIBLE_ONLY);
        }
        // #sijapp cond.end #
    }

    // #sijapp cond.if modules_XSTATUSES is "true" #
    public static final IcqXStatus xstatus = new IcqXStatus();

    protected void s_updateXStatus() {
        sendCaps();
        sendNewXStatus();
    }

    // #sijapp cond.end #
    protected void s_setPrivateStatus() {
        sendPrivateStatus(getIcqPrivateStatus());
    }

    private byte getPrivateStatusByStatus() {
        switch (getProfile().statusIndex) {
            case StatusInfo.STATUS_INVIS_ALL:
                return Icq.PSTATUS_NONE;
            case StatusInfo.STATUS_INVISIBLE:
                return Icq.PSTATUS_VISIBLE_ONLY;
        }
        return -1;
    }

    public byte getIcqPrivateStatus() {
        byte p = getPrivateStatusByStatus();
        if (-1 != p) return p;
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        switch (getPrivateStatus()) {
            case StatusInfo.PSTATUS_ALL:
                return Icq.PSTATUS_ALL;
            case StatusInfo.PSTATUS_VISIBLE_ONLY:
                return Icq.PSTATUS_VISIBLE_ONLY;
            case StatusInfo.PSTATUS_NOT_INVISIBLE:
                return Icq.PSTATUS_NOT_INVISIBLE;
            case StatusInfo.PSTATUS_CL_ONLY:
                return Icq.PSTATUS_CL_ONLY;
            case StatusInfo.PSTATUS_NONE:
                return Icq.PSTATUS_NONE;
        }
        // #sijapp cond.end #   
        return Icq.PSTATUS_NOT_INVISIBLE;
    }

    // #sijapp cond.if modules_CLIENTS is "true" #
    private Client clientMasking = new Client();

    public void setClient(int clientIndex) {
        p.clientId = clientIndex;
        p.save();
        clientMasking.setClient(p.clientId, null);
        if (isConnected()) {
            requestSimpleAction(getStatusPacket());
            sendCaps();
        }
    }

    public Client getClient() {
        return clientMasking;
    }
    // #sijapp cond.end #

    public int privateStatusId = 0;

    private int ssiListLastChangeTime = -1;
    private int ssiNumberOfItems = 0;

    /* Returns the id number #1 which identifies (together with id number #2)
the saved contact list version */
    public int getSsiListLastChangeTime() {
        return ssiListLastChangeTime;
    }

    /* Returns the id number #2 which identifies (together with id number #1)
the saved contact list version */
    public int getSsiNumberOfItems() {
        return ssiNumberOfItems;
    }

    public void setContactListInfo(int timestamp, int numberOfItems) {
        ssiListLastChangeTime = timestamp;
        ssiNumberOfItems = numberOfItems;
    }

    public Group createGroup(String name) {
        Group g = new Group(name);
        g.setMode(Group.MODE_FULL_ACCESS);
        g.setGroupId(createRandomId());
        return g;
    }

    public Contact createContact(String uin, String name) {
        name = (null == name) ? uin : name;
        try {
            IcqContact c = new IcqContact(uin);
            c.init(-1, Group.NOT_IN_GROUP, name, false);
            return c;
        } catch (Exception e) {
            // Message from non-icq contact
            return null;
        }
    }

    private boolean isExistId(final int id) {
        if (id == privateStatusId) {
            return true;
        }
        for (int i = groups.size() - 1; i >= 0; --i) {
            Group group = (Group) groups.elementAt(i);
            if (group.getId() == id) {
                return true;
            }
        }
        for (int i = contacts.size() - 1; i >= 0; --i) {
            IcqContact item = (IcqContact) contacts.elementAt(i);
            if ((item.getContactId() == id)) {
                return true;
            }
        }
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        if (isExistId(invisibleList, id) || isExistId(visibleList, id)
                || isExistId(ignoreList, id)) {
            return true;
        }
        // #sijapp cond.end #
        return false;
    }

    // #sijapp cond.if modules_SERVERLISTS is "true" #
    private boolean isExistId(Vector<PrivacyItem> list, int id) {
        for (int i = list.size() - 1; 0 <= i; --i) {
            if (((PrivacyItem) list.elementAt(i)).id == id) {
                return true;
            }
        }
        return false;
    }

    // #sijapp cond.end #
    // Create a random id which is not used yet
    public int createRandomId() {
        int id;
        do {
            // Max value is probably 0x7FFF, lowest value is unknown.
            // We use range 0x1000-0x7FFF.
            // From miranda source
            id = Util.nextRandInt() % 0x6FFF + 0x1000;
        } while (isExistId(id));
        return id;
    }


    protected void loadProtocolData(byte[] data) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        DataInputStream dis = new DataInputStream(bais);
        ssiListLastChangeTime = dis.readInt();
        ssiNumberOfItems = dis.readUnsignedShort();
    }

    protected byte[] saveProtocolData() throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(getSsiListLastChangeTime());
        dos.writeShort((short) getSsiNumberOfItems());
        return baos.toByteArray();
    }

    protected Contact loadContact(DataInputStream dis) throws Exception {
        int contactId = dis.readInt();
        int groupId = dis.readInt();
        byte flags = dis.readByte();
        String uin = dis.readUTF();
        String name = dis.readUTF();
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        int visibleId = dis.readInt();
        int invisibleId = dis.readInt();
        int ignoreId = dis.readInt();
        // #sijapp cond.end #

        IcqContact contact = (IcqContact) createContact(uin, name);
        contact.setContactId(contactId);
        contact.setGroupId(groupId);
        contact.setBooleanValues(flags);
        contact.setName(name);
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        setVisibleId(contact, visibleId);
        setInvisibleId(contact, invisibleId);
        setIgnoreId(contact, ignoreId);
        // #sijapp cond.end #
        contact.setOfflineStatus();
        return contact;
    }

    protected void saveContact(DataOutputStream out, Contact contact) throws Exception {
        IcqContact icqContact = ((IcqContact) contact);
        out.writeByte(0);
        out.writeInt(icqContact.getContactId());
        out.writeInt(icqContact.getGroupId());
        out.writeByte(icqContact.getBooleanValues());
        out.writeUTF(icqContact.getUserId());
        out.writeUTF(icqContact.getName());
        // #sijapp cond.if modules_SERVERLISTS is "true" #
        out.writeInt(getVisibleId(icqContact));
        out.writeInt(getInvisibleId(icqContact));
        out.writeInt(getIgnoreId(icqContact));
        // #sijapp cond.end #
    }

    @Override
    public void getAvatar(UserInfo userInfo) {
        // String uin = userInfo.uin;
        // String url = "http://api.icq.net/expressions/get?f=native&type=buddyIcon&t=" + uin;
        // new jimm.ui.timers.GetVersion(jimm.ui.timers.GetVersion.TYPE_AVATAR, userInfo, url).get();
    }


    //    protected void s_searchUsers(Search cont) {
//        String userId = cont.getSearchParam(Search.UIN);
//        if (null != userId) {
//            UserInfo userInfo = new UserInfo(this);
//            userInfo.uin = userId;
//            cont.addResult(userInfo);
//            cont.finished();
//        } else {
//            requestSimpleAction(new SearchAction(cont));
//        }
//    }
    protected void s_removeGroup(Group group) {
        requestSimpleAction(new UpdateContactListAction(group, UpdateContactListAction.ACTION_DEL));
    }

    protected void s_renameGroup(Group group, String name) {
        group.setName(name);
        IcqAction act = new UpdateContactListAction(group, UpdateContactListAction.ACTION_RENAME);
        requestSimpleAction(act);
    }

    /* Sets new contact name */
    protected void s_renameContact(Contact contact, String name) {
        contact.setName(name);
        /* Try to save ContactList to server */
        requestSimpleAction(new UpdateContactListAction(this, contact,
                UpdateContactListAction.ACTION_RENAME));
    }

    protected void s_moveContact(Contact contact, Group to) {
        Group oldGroup = getGroup(contact);
        Group newGroup = to;
        requestSimpleAction(new UpdateContactListAction((IcqContact) contact, oldGroup, newGroup));
    }

    public void saveUserInfo(UserInfo userInfo) {
        requestSimpleAction(new SaveInfoAction(userInfo));
    }

    private static final int[] statuses = {
            StatusInfo.STATUS_CHAT,
            StatusInfo.STATUS_ONLINE,
            StatusInfo.STATUS_AWAY,
            StatusInfo.STATUS_NA,
            StatusInfo.STATUS_OCCUPIED,
            StatusInfo.STATUS_DND,
            StatusInfo.STATUS_EVIL,
            StatusInfo.STATUS_DEPRESSION,
            StatusInfo.STATUS_LUNCH,
            StatusInfo.STATUS_HOME,
            StatusInfo.STATUS_WORK,
            StatusInfo.STATUS_INVISIBLE,
            StatusInfo.STATUS_INVIS_ALL};

    public int[] getStatusList() {
        return statuses;
    }

    protected void s_sendTypingNotify(Contact to, boolean isTyping) {
        sendBeginTyping(to, isTyping);
    }

    /**
     * ************************************************************************
     */
    public SnacPacket getStatusPacket() {
        long status = 0x10000000 | IcqStatusInfo.getNativeStatus(getProfile().statusIndex);
        // Send a CLI_SETSTATUS packet
        byte[] data = new byte[4 + 4 + 4 + 0x25];//CLI_SETSTATUS_DATA;
        Util.putDWordBE(data, 0, 0x00060004);
        Util.putDWordBE(data, 4, status);
        Util.putDWordBE(data, 8, 0x000C0025);
        Util.putDWordBE(data, 23, 0xE36C96A9);

        Util.putWordBE(data, 21, 0x0009);
        Util.putDWordBE(data, 35, 0xFFFFFFFE);
        Util.putDWordBE(data, 39, 0x00100000);
        Util.putDWordBE(data, 43, 0xFFFFFFFE);
        // #sijapp cond.if modules_CLIENTS is "true" #
//        ClientDetector.instance.setClientDataStatus(data, getClient().getClientIndex());
        // #sijapp cond.end #
        return new SnacPacket(SnacPacket.SERVICE_FAMILY, SnacPacket.CLI_SETSTATUS_COMMAND, data);
    }

    public SnacPacket getCapsPacket() {
        Vector<GUID> guids = new Vector<GUID>();

        boolean isJimm = true;
        // #sijapp cond.if modules_CLIENTS is "true" #
//        int clientIndex = getClient().getClientIndex();
//        isJimm = !ClientDetector.instance.setClientGUID(guids, clientIndex);
        // #sijapp cond.end #
        if (isJimm) {
            guids.addElement(GUID.CAP_AIM_ISICQ);
            guids.addElement(GUID.CAP_AIM_SERVERRELAY);
            // #sijapp cond.if modules_XSTATUSES is "true" #
            guids.addElement(GUID.CAP_XTRAZ);
            // #sijapp cond.end #
            guids.addElement(GUID.CAP_JIMM);
            // #sijapp cond.if modules_DEBUGLOG is "true" #
            guids.addElement(GUID.IAMTESTER);
            // #sijapp cond.end #
            //guids.addElement(GUID.CAP_DC);
//            if (!Options.getBoolean(Options.OPTION_CP1251_HACK)) {
            guids.addElement(GUID.CAP_UTF8);
//            }
        }

        // #sijapp cond.if modules_XSTATUSES is "true" #
        GUID x = Icq.xstatus.getIcqGuid(p.xstatusIndex);
        if (null != x) {
            guids.addElement(x);
        }
        // #sijapp cond.end #

        if (p.typingMode) {
            guids.addElement(GUID.CAP_MTN);
        }


        // Send a CLI_SETUSERINFO packet
        // Set version information to this packet in our capability
        int extStatus = IcqStatusInfo.getExtStatus(getProfile().statusIndex);
        int extStatusCount = (0 == extStatus ? 0 : 1);
        int guidsCount = guids.size() + extStatusCount;
        byte[] packet = new byte[guidsCount * 16 + 4];
        packet[0] = 0x00;
        packet[1] = 0x05;
        packet[2] = (byte) ((guidsCount * 16) / 0x0100);
        packet[3] = (byte) ((guidsCount * 16) % 0x0100);

        if (0 != extStatus) {
            System.arraycopy(GUID.CAP_QIP_STATUS.toByteArray(), 0, packet, 4, 16);
            Util.putByte(packet, 4 + 15, extStatus); // status
        }

        for (int i = extStatusCount; i < guidsCount; ++i) {
            System.arraycopy(((GUID) guids.elementAt(i - extStatusCount)).toByteArray(), 0, packet, i * 16 + 4, 16);
        }

        return new SnacPacket(SnacPacket.LOCATION_FAMILY, SnacPacket.CLI_SETUSERINFO_COMMAND, packet);
    }

    public static final byte PSTATUS_ALL = 0x01;
    public static final byte PSTATUS_NONE = 0x02;
    public static final byte PSTATUS_VISIBLE_ONLY = 0x03;
    public static final byte PSTATUS_NOT_INVISIBLE = 0x04;
    public static final byte PSTATUS_CL_ONLY = 0x05;

    public SnacPacket getPrivateStatusPacket(byte status) {
        int id = privateStatusId;
        int cmd = SnacPacket.CLI_ROSTERUPDATE_COMMAND;
        if (id == 0) {
            id = createRandomId();
            cmd = SnacPacket.CLI_ROSTERADD_COMMAND;
            privateStatusId = id;
        }
        Util stream = new Util();
        stream.writeWordBE(0);    // name (null)
        stream.writeWordBE(0);    // GroupID
        stream.writeWordBE(id);   // EntryID
        stream.writeWordBE(4);    // EntryType
        stream.writeWordBE(5);    // Length in bytes of following TLV
        stream.writeTLVByte(0xCA, status);
        return new SnacPacket(SnacPacket.SSI_FAMILY, cmd, stream.toByteArray());
    }

    private void sendCaps() {
        requestSimpleAction(getCapsPacket());
    }

    private void sendStatus() {
        long status = 0x10000000 | IcqStatusInfo.getNativeStatus(getProfile().statusIndex);
        Util data = new Util();
        data.writeTLVDWord(0x0006, status);

        SnacPacket p = new SnacPacket(SnacPacket.SERVICE_FAMILY, SnacPacket.CLI_SETSTATUS_COMMAND, data.toByteArray());
        requestSimpleAction(p);
    }

    private void sendPrivateStatus(byte status) {
        requestSimpleAction(getPrivateStatusPacket(status));
    }

    private static final byte[] MTN_PACKET_BEGIN = {
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0x00, (byte) 0x01
    };

    private void sendBeginTyping(Contact contact, boolean isTyping) {
        Util stream = new Util();
        stream.writeByteArray(MTN_PACKET_BEGIN);
        stream.writeShortLenAndUtf8String(contact.getUserId());
        stream.writeWordBE(isTyping ? 0x0002 : 0x0000);
        SnacPacket snacPkt = new SnacPacket(0x0004, 0x0014, stream.toByteArray());
        requestSimpleAction(snacPkt);
    }

    // #sijapp cond.if modules_XSTATUSES is "true" #
    private void addMoodToStatus(Util out, int xStatus, String msg) {
        int moodIndex = Icq.xstatus.getIcqMood(xStatus);
        String mood = (moodIndex < 0) ? "" : ("0icqmood" + moodIndex);
        msg = (msg.length() < 250) ? msg : (msg.substring(0, 250 - 3) + "...");
        /* xStatus */
        byte[] moodArr = StringConvertor.stringToByteArrayUtf8(mood);
        byte[] xMsgArr = StringConvertor.stringToByteArrayUtf8(msg);
        int xMsgLen = (0 == xMsgArr.length) ? 0 : (2 + xMsgArr.length + 2);
        int tlvLen = (2 + 1 + 1 + xMsgLen)
                + (2 + 1 + 1 + moodArr.length);

        // TLV (0x1D)
        out.writeWordBE(0x1D);
        out.writeWordBE(tlvLen);

        // subTLV 0x000E (mood)
        out.writeWordBE(0x000E);
        out.writeWordBE(moodArr.length);
        out.writeByteArray(moodArr);

        // subTLV 0x0002 (text)
        out.writeWordBE(0x0002);
        out.writeByte(0x04);
        out.writeByte(xMsgLen);
        if (0 < xMsgLen) {
            out.writeWordBE(xMsgArr.length);
            out.writeByteArray(xMsgArr);
            out.writeWordBE(0x0000);
        }
    }

    public SnacPacket getNewXStatusPacket(int xStatus, String msg) {
        Util mood = new Util();
        addMoodToStatus(mood, xStatus, msg.trim());
        return new SnacPacket(SnacPacket.SERVICE_FAMILY,
                SnacPacket.CLI_SETSTATUS_COMMAND, mood.toByteArray());
    }

    private void sendNewXStatus() {
        int index = p.xstatusIndex;
        String title = p.xstatusTitles[index];
        String desc = p.xstatusDescriptions[index];
        title = StringConvertor.notNull(title);
        desc = StringConvertor.notNull(desc);
        String text = (title + " " + desc).trim();
        requestSimpleAction(getNewXStatusPacket(index, text));
    }
    // #sijapp cond.end#

    void sendRemoveMePacket(String uin) {
        //	Get byte Arrys from the stuff we need the length of
        byte[] uinRaw = StringConvertor.stringToByteArray(uin);

        byte[] buf = new byte[1 + uinRaw.length];

        // Assemble the packet
        Util.putByte(buf, 0, uinRaw.length);
        System.arraycopy(uinRaw, 0, buf, 1, uinRaw.length);
        SnacPacket pkt = new SnacPacket(SnacPacket.SSI_FAMILY,
                SnacPacket.CLI_REMOVEME_COMMAND, 0x00000003, buf);
        requestSimpleAction(pkt);
    }


    /**
     * ************************************************************************
     */
    // #sijapp cond.if modules_SERVERLISTS is "true" #
    private Vector<PrivacyItem> ignoreList = new Vector<PrivacyItem>();
    private Vector<PrivacyItem> invisibleList = new Vector<PrivacyItem>();
    private Vector<PrivacyItem> visibleList = new Vector<PrivacyItem>();

    public void setPrivacyLists(Vector<PrivacyItem> ignore, Vector<PrivacyItem> invisible, Vector<PrivacyItem> visible) {
        ignoreList = ignore;
        invisibleList = invisible;
        visibleList = visible;
    }

    public void setIgnoreId(IcqContact c, int id) {
        setPrivacy(ignoreList, c, Contact.SL_IGNORE, id);
    }

    public void setVisibleId(IcqContact c, int id) {
        setPrivacy(visibleList, c, Contact.SL_VISIBLE, id);
    }

    public void setInvisibleId(IcqContact c, int id) {
        setPrivacy(invisibleList, c, Contact.SL_INVISIBLE, id);
    }

    private void setPrivacy(Vector<PrivacyItem> list, Contact c, byte l, int id) {
        PrivacyItem item = getListItem(list, c);
        if (0 == id) {
            invisibleList.removeElement(item);
        } else if (null == item) {
            item = new PrivacyItem(c.getUserId(), id);
            list.addElement(item);
        } else {
            item.id = id;
        }
        c.setBooleanValue(l, id != 0);
    }

    private PrivacyItem getListItem(Vector<PrivacyItem> list, Contact c) {
        String uin = c.getUserId();
        PrivacyItem item;
        for (int i = list.size() - 1; 0 <= i; --i) {
            item = (PrivacyItem) list.elementAt(i);
            if (uin.equals(item.userId)) {
                item.userId = uin;
                return item;
            }
        }
        return null;
    }

    public int getIgnoreId(Contact c) {
        PrivacyItem item = getListItem(ignoreList, c);
        return (null == item) ? 0 : item.id;
    }

    public int getVisibleId(Contact c) {
        PrivacyItem item = getListItem(visibleList, c);
        return (null == item) ? 0 : item.id;
    }

    public int getInvisibleId(Contact c) {
        PrivacyItem item = getListItem(invisibleList, c);
        return (null == item) ? 0 : item.id;
    }
    // #sijapp cond.end #

    /**
     * ************************************************************************
     */

    private void request(String userId, int type, boolean auth) {
        String reason = "";
        SystemNotice notice = new SystemNotice(this, type, userId, reason, auth);
        requestSimpleAction(new SysNoticeAction(notice));
    }

    protected void requestAuth(String userId) {
        request(userId, SystemNotice.SYS_NOTICE_REQUAUTH, false);
    }

    protected void grandAuth(String userId) {
        request(userId, SystemNotice.SYS_NOTICE_AUTHORISE, true);
    }

    protected void denyAuth(String userId) {
        request(userId, SystemNotice.SYS_NOTICE_AUTHORISE, false);
    }

    public void showUserInfo(Contact contact, Activity activity) {
        UserInfo data = null;
        if (isConnected()) {
            data = getUserInfo(contact, activity);
            data.setProfileViewToWait();

        } else {
            data = new UserInfo(this, activity);
            data.nick = contact.getName();
            data.uin = contact.getUserId();
            data.updateProfileView();
        }
        data.getDialog().show();
    }

    public void showStatus(Contact contact) {
        StatusView statusView = new StatusView(this, contact);

        _updateStatusView(statusView, contact);
        statusView.showIt();

        if ((XStatusInfo.XSTATUS_NONE != contact.getXStatusIndex())
                && (null == contact.getXStatusText())
                && isMeVisible(contact)) {
            requestXStatusMessage(statusView, contact);
        }
    }

    public void updateStatusView(StatusView statusView, Contact contact) {

        _updateStatusView(statusView, contact);
    }

    private void _updateStatusView(StatusView statusView, Contact contact) {
//        int index = statusView.getCurrItem();
//
//        statusView.init(this, contact);
        statusView.addContactStatus();
//
//        IcqOnlineInfo online = ((IcqContact)contact).onlineInfo;
//        if (contact.isOnline()) {
//            int happy = online.getHappyIcon();
//            if (-1 != happy) {
////                statusView.addPlain(happy, JimmApplication.getContext(null).getString(R.string.status_happy_flag));
//            }
        String statusText = contact.getStatusText();
        if (!TextUtils.isEmpty(statusText)) {
            statusView.addStatusText(statusText);
        }
//
        if (XStatusInfo.XSTATUS_NONE != contact.getXStatusIndex()) {
            statusView.addXStatus();
            String xText = contact.getXStatusText();
            if (!TextUtils.isEmpty(xText)) {
                statusView.addStatusText(xText);
            }
        }
//        }
//
//        // #sijapp cond.if modules_CLIENTS is "true" #
//        statusView.addBr();
//        Client client = ((IcqContact)contact).getClient();
////        statusView.addClient(client.getIcon(), client.getFullName());
//        if (contact.isOnline()) {
//            long now = Util.createCurrentDate(false);
//            long signonTime = online.signOnTime;
//            if (0 < signonTime) {
//                statusView.addInfo("li_signon_time", Util.getDateString(signonTime, false));
//            }
//
//            /* online time */
//            long onlineTime = online.onlineTime;
//            if (0 < onlineTime) {
//                statusView.addInfo("li_online_time", Util.longitudeToString(now - onlineTime));
//            }
//
//            /* idle time */
//            long idleTime = online.idleTime;
//            if (0 < idleTime) {
//                statusView.addInfo("li_idle_time",   Util.longitudeToString(now - idleTime));
//            }
//        }
//        // #sijapp cond.end #
////        statusView.setCurrentItem(index);
////        statusView.unlock();
    }

    @Override
    public int getNodeWeight() {
        return 0;
    }
}
// #sijapp cond.end #
